package com.yqh.utils.zookeeper;

import com.yqh.Constant;
import com.yqh.exceptions.ZookeeperException;
import lombok.extern.slf4j.Slf4j;
import org.apache.zookeeper.*;

import java.io.IOException;
import java.util.List;
import java.util.concurrent.CountDownLatch;

/**
 * @author 阳秋豪
 * @createTime 2024/2/29
 */
@Slf4j
public class ZookeeperUtils {
    /**
     * 使用默认配置创建 zookeeper 实例
     *
     * @return zookeeper 实例
     */
    public static ZooKeeper createZookeeper() {
        // 默认连接参数 -- 127.0.0.1:2181
        String connectString = Constant.DEFAULT_ZK_CONNECT;
        // 默认超时时间 -- 10秒
        int timeout = Constant.TIME_OUT;
        return createZookeeper(connectString, timeout);
    }

    /**
     * 自定义配置创建 zookeeper 实例
     *
     * @param connectString 连接参数
     * @param timeout       超时时间
     * @return zookeeper 实例
     */
    public static ZooKeeper createZookeeper(String connectString, int timeout) {
        // 计时
        CountDownLatch countDownLatch = new CountDownLatch(1);
        try {
            // 创建一个 zookeeper 实例，并设置连接参数、超时时间、监听事件 -- 建立连接
            final ZooKeeper zookeeper = new ZooKeeper(connectString, timeout, watchedEvent -> {
                // 只有连接成功才放行
                if (watchedEvent.getState() == Watcher.Event.KeeperState.SyncConnected) {
                    log.debug("客户端连接成功");
                    countDownLatch.countDown();
                }
            });
            // 阻塞线程直到计时到零或者调用 countDown() 方法时结束阻塞
            countDownLatch.await();
            return zookeeper;
        } catch (IOException | InterruptedException e) {
            log.error("创建 zookeeper 实例时产生异常：", e);
            throw new ZookeeperException();
        }
    }

    /**
     * 创建一个 zookeeper 节点的工具方法
     *
     * @param zookeeper     zookeeper 实例
     * @param zookeeperNode zookeeper 节点
     * @param watcher       watch 实例
     * @param createMode    节点的类型
     */
    public static void createNode(ZooKeeper zookeeper, ZookeeperNode zookeeperNode, Watcher watcher, CreateMode createMode) {
        try {
            if (zookeeper.exists(zookeeperNode.getPath(), watcher) == null) {
                // 用给定的路径创建一个节点,节点数据将是给定的数据，节点acl将是给定的acl,flags参数指定创建的节点是否为临时节点。
                zookeeper.create(
                        zookeeperNode.getPath(), // 节点的路径
                        zookeeperNode.getDate(), // 节点的初始数据
                        ZooDefs.Ids.OPEN_ACL_UNSAFE, // 节点的Acl
                        createMode // 指定要创建的节点是临时的还是顺序的
                );
                log.info("{} -- 节点创建成功", zookeeperNode.getPath());
            } else {
                if (log.isDebugEnabled()) {
                    log.info("{} -- 节点已存在，无需创建", zookeeperNode.getPath());
                }
            }
        } catch (KeeperException | InterruptedException e) {
            log.error("节点创建时发生异常 -- " + e);
            throw new ZookeeperException();
        }
    }

    /**
     * 关闭zookeeper
     *
     * @param zookeeper 实例
     */
    public static void closeZookeeper(ZooKeeper zookeeper) {
        try {
            zookeeper.close();
        } catch (InterruptedException e) {
            log.info("关闭 zookeeper 时发生异常 -- " + e);
            throw new ZookeeperException();
        }
    }

    /**
     * 判断节点是否存在
     *
     * @param zookeeper zookeeper 实例
     * @param nodePath  需要判断的 zookeeper 节点路径
     * @param watcher   节点的 watcher 机制（观察者）
     * @return true -- 存在 false -- 不存在
     */
    public static boolean exists(ZooKeeper zookeeper, String nodePath, Watcher watcher) {
        try {
            return zookeeper.exists(nodePath, watcher) != null;
        } catch (KeeperException | InterruptedException e) {
            log.error("判断节点[{}]是否存在发生异常", nodePath, e);
            throw new ZookeeperException(e);
        }
    }

    /**
     * 查询一个节点的子元素
     *
     * @param zookeeper       zk 实例
     * @param serviceNodePath 服务节点路径
     * @return 子元素列表
     */
    public static List<String> getChildren(ZooKeeper zookeeper, String serviceNodePath, Watcher watcher) {
        try {
            return zookeeper.getChildren(serviceNodePath, watcher);
        } catch (KeeperException | InterruptedException e) {
            log.error("获取【{}】节点子元素时发生异常 -- ", serviceNodePath, e);
            throw new ZookeeperException(e);
        }
    }
}
